﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;

// Battle.cs
// Contains the visual representation and logic for the battle system.
namespace RTS_RPG
{
    public class Battle
    {
        GraphicsDeviceManager graphics;
        public Battle_TextureManager textures;
        public Battle_MouseManager mouse;
        public Battle_Drawer drawer;
        public Battle_HUD hud;
        public PlayerStats currentPlayer;
        public Overworld_Creature enemies;

        public IServiceProvider services;
        // private SpriteBatch spriteBatch;
        // public SpriteFont font { get; private set; }

        //public List<Unit> startingUnits;   // May want to use at some point to clean up dead units in "units"
        public List<Battle_Unit> units;
        public List<Battle_Unit> friendly;
        public List<Battle_Unit> enemy;
        public List<Battle_Unit> selected;

        public Boolean post_battle;
        public Boolean battle_over;
        // private bool pause;      // Pausing now handled by Overworld
        private bool splashScreen;
        private const int VIC_COUNT_TIME = 200;
        private int vic_countdown;

        public Battle(GraphicsDeviceManager g_manager, PlayerStats currP, IServiceProvider s)
        {
            services = s;
            graphics = g_manager;
            currentPlayer = currP;
            textures = new Battle_TextureManager(services);
            textures.LoadContent();
            hud = new Battle_HUD(this);
            // Content.RootDirectory = "Content";  // Should already be set in RPGGame I think //
            
            //Program.SCREENWIDTH = Window.ClientBounds.Width;
            //Program.SCREENHEIGHT = Window.ClientBounds.Height;
            //graphics.PreferredBackBufferWidth = Program.SCREENWIDTH;
            //graphics.PreferredBackBufferHeight = Program.SCREENHEIGHT;
            //graphics.IsFullScreen = true;
            splashScreen = true;
            vic_countdown = VIC_COUNT_TIME;
            post_battle = false;
            battle_over = false;
            //content = new ContentManager(Services);
            //Window.AllowUserResizing = true;

            enemies = null;
            Initialize();
        }

        public Battle(GraphicsDeviceManager g_manager, PlayerStats currP, IServiceProvider s, Overworld_Creature creature)
        {
            services = s;
            graphics = g_manager;
            currentPlayer = currP;
            textures = new Battle_TextureManager(services);
            textures.LoadContent();
            hud = new Battle_HUD(this);
            // Content.RootDirectory = "Content";  // Should already be set in RPGGame I think //

            //Program.SCREENWIDTH = Window.ClientBounds.Width;
            //Program.SCREENHEIGHT = Window.ClientBounds.Height;
            //graphics.PreferredBackBufferWidth = Program.SCREENWIDTH;
            //graphics.PreferredBackBufferHeight = Program.SCREENHEIGHT;
            //graphics.IsFullScreen = true;
            splashScreen = true;
            vic_countdown = VIC_COUNT_TIME;
            post_battle = false;
            battle_over = false;
            //content = new ContentManager(Services);
            //Window.AllowUserResizing = true;

            enemies = creature;
            Initialize();
        }

        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected void Initialize()
        {
            // base.Initialize();
            // pause = false;        // Pausing now handled by Overworld
        }

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        public void Update(GameTime gameTime, MouseState mstate, KeyboardState kstate)
        {

            if (splashScreen)
            {
                splashScreen = hud.updateSplashScreen(kstate);
                return;
            }

            mouse.Update(gameTime, mstate, kstate);

            if (post_battle) 
            {
                post_battle_screen_update(kstate);
                return;
            }

            /*if (!pause && kstate.IsKeyDown(Keys.P)) pause = true;
            if (pause && kstate.IsKeyDown(Keys.Enter)) { pause = false; return; }*/
            if (/*pause || */ gameOver()) return;

            // Space selects all friendly units
            if (kstate.IsKeyDown(Keys.Space))
            {
                selected.Clear();
                foreach (Battle_Unit unit in friendly)
                    selected.Add(unit);
            }

            foreach (Battle_Unit unit in units)
                unit.Update(gameTime);

            if (vic_countdown < VIC_COUNT_TIME || victory())
                --vic_countdown;
            if (vic_countdown < 0)
                post_battle = true;

            handleAI(kstate);
        }

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        public void Draw(GameTime gameTime, GraphicsDevice g_device, SpriteBatch spriteBatch)
        {
            g_device.Clear(Color.Black);

            /*** SpriteBatch Begins ***/
            // spriteBatch.Begin();

            if (splashScreen)
            {
                hud.drawSplashScreen(spriteBatch);
                return;
            }

            // For testing collisions
            spriteBatch.Draw(textures.blankBoxSmall, 
                new Vector2(Program.SCREENWIDTH / 2 - 64, Program.SCREENHEIGHT / 2 - 128), Color.Azure);
            spriteBatch.Draw(textures.blankBoxSmall,
                new Vector2(Program.SCREENWIDTH / 2 + 64, Program.SCREENHEIGHT / 2 - 128), Color.Azure);
            spriteBatch.Draw(textures.blankBoxSmall,
                new Vector2(Program.SCREENWIDTH / 2 + 64, Program.SCREENHEIGHT / 2 + 0), Color.Azure);
            spriteBatch.Draw(textures.blankBoxSmall,
                new Vector2(Program.SCREENWIDTH / 2 - 64, Program.SCREENHEIGHT / 2 + 0), Color.Azure);

            drawer.Draw(gameTime, spriteBatch);
            
            // Deal with the mouse and the HUD and stuff
            mouse.drawSelectBox(spriteBatch);

            hud.Draw(gameTime, spriteBatch);

            if (gameOver())
            {
                spriteBatch.DrawString(textures.font2, "GAME OVER", 
                    new Vector2(Program.SCREENWIDTH / 2 - 65, Program.SCREENHEIGHT / 2 - 40), 
                    Color.Red, 0.0f, new Vector2(0, 0), 1.5f, new SpriteEffects(), 0.0f);
            }
            else if (vic_countdown <= 100)
            {
                spriteBatch.DrawString(textures.font2, "Victory!",
                    new Vector2(Program.SCREENWIDTH / 2 - 65, Program.SCREENHEIGHT / 2 - 40),
                    Color.Blue, 0.0f, new Vector2(0, 0), 2.0f, new SpriteEffects(), 0.0f);
            }
            /*else if (pause)
            {
                spriteBatch.DrawString(font, "PAUSED",
                    new Vector2(Program.SCREENWIDTH / 2 - 50, Program.SCREENHEIGHT / 2 - 61),
                    Color.Blue, 0.0f, new Vector2(0, 0), 1.5f, new SpriteEffects(), 0.0f);
                spriteBatch.DrawString(font, "Press 'enter' to resume game...",
                    new Vector2(Program.SCREENWIDTH / 2 - 111, Program.SCREENHEIGHT / 2 - 10), Color.PowderBlue);
            }*/

            if (post_battle)
            {
                post_battle_screen_draw(spriteBatch);
            }

            mouse.Draw(spriteBatch);

            // spriteBatch.End();
            /*** SpriteBatch Ends ***/
        }


        /****************** Update Helpers **********************************************/

        private void handleAI(KeyboardState kstate)
        {
            if (kstate.IsKeyDown(Keys.Down))
            {
                foreach (Battle_Unit unit in selected)
                {
                    unit.AIon = false;
                    unit.currentTarget = null;
                    unit.targetQueue.Clear();
                    unit.destination = new Vector2(Program.NaN, Program.NaN);
                }
            }
            else if (kstate.IsKeyDown(Keys.Up))
            {
                foreach (Battle_Unit unit in selected)
                    unit.AIon = true;
            }
        }

        private Boolean gameOver()
        {
            foreach (Battle_Unit unit in friendly)
            {
                if (!unit.dead) return false;
            }
            return true;
        }

        private Boolean victory()
        {
            foreach (Battle_Unit unit in enemy)
            {
                if (!unit.dead) return false;
            }
            return true;
        }
        

        /********* Splash Screen and Initializer Helpers ********************************/

        public void postSplashInitialize()
        {
            units = new List<Battle_Unit>();
            selected = new List<Battle_Unit>();
            friendly = new List<Battle_Unit>();
            enemy = new List<Battle_Unit>();

            mouse = new Battle_MouseManager(this);
            drawer = new Battle_Drawer(this);

            int i = 50;
            foreach (Character unit in currentPlayer.party)
            {
                Battle_Unit new_unit = new Battle_Unit(this, unit, new Vector2(200, i), false);
                units.Add(new_unit);
                friendly.Add(new_unit);
                i += 50;
            }

            if (enemies == null)
            {
                for (i = 50; i <= 400; i += 50)
                {
                    /*Battle_Unit unit = new Battle_Unit(this, null,
                        null, null, 20, 1, 4, 50, false, new Vector2(200, i));
                    units.Add(unit);
                    friendly.Add(unit);*/
                    Battle_Unit unit = new Battle_Unit(this, null,
                        null, null, 30, 3, 0, 4, 50, true, new Vector2(Program.SCREENWIDTH - 200, i));
                    units.Add(unit);
                    enemy.Add(unit);
                }
            }
            else
            {
                i = 50;
                foreach(Character unit in enemies.enemy_party)
                {
                    Battle_Unit new_unit = new Battle_Unit(this, unit, new Vector2(Program.SCREENWIDTH - 200, i), true);
                    units.Add(new_unit);
                    enemy.Add(new_unit);
                    i += 50;
                }
            }
        }

        private void post_battle_screen_update(KeyboardState kstate)
        {
            if (kstate.IsKeyDown(Keys.Enter))
            {
                battle_over = true;

                int exp_gained = 0;
                foreach(Battle_Unit unit in enemy) 
                {
                    if (unit.master == null) exp_gained += 5;
                    else exp_gained += unit.master.level * 4;
                }

                foreach (Character member in currentPlayer.party)
                {
                    // if a unit is dead, set its hp to 1 after the battle.
                    if (member.hp.X <= 0) member.hp.X = 1;
                    else
                    {
                        member.xp.X += exp_gained;
                        member.check_level_up();
                    }
                }
            }
        }

        private void post_battle_screen_draw(SpriteBatch spBatch)
        {
            spBatch.Draw(textures.HUD_background, new Vector2(400, 400), Color.Azure);
            spBatch.DrawString(textures.font, "ENTER -Accept", new Vector2(450, 450), Color.WhiteSmoke);
        }

        public Boolean collision(Vector2 location, Battle_Unit check)
        {
            foreach (Battle_Unit unit in units) 
            {
                if (unit.dead || unit == check) continue;
                /*if (location.X + (check.image.Width / 2 - 4)  > unit.location.X - (unit.image.Width / 2 - 4)  &&
                    location.X - (check.image.Width / 2 - 4)  < unit.location.X + (unit.image.Width / 2 - 4)  &&
                    location.Y + (check.image.Height / 2 - 4) > unit.location.Y - (unit.image.Height / 2 - 4) &&
                    location.Y - (check.image.Height / 2 - 4) < unit.location.Y + (unit.image.Height / 2 - 4)  )*/
                if (Vector2.Distance(location, unit.location) < (check.image.Width / 2) + (unit.image.Width / 2) - 6)
                    return true;
            }
            return false;
        }
    }
}
